/*
* Name: TaskHandle.cpp
* Created by 勇 on 2022/11/17.
* Description: 主要程序
*
*/
#include "TaskHandle.h"

/**
 * 是否自动连接wifi
 */
boolean isAutoWiFi = false;

/**
 * wifi配置文件
 */
Config_type configType;

/**
 * 设备唯一ID
 */
String deviceChipId;

/**
 * 连接wifi累计
 */
int timeTotal = 0;

/**
 * 配网计次
 */
int smartConfigTotal = 0;

/**
 * 上电启动
 */
boolean isFirst = true;

/**
 * mqtt客户端
 */
WiFiClient espClient;
PubSubClient mqttClient(espClient);

/**
 * mqtt订阅 发布主题
 */
char mqttPubTopic[128];
char mqttSubTopic[128];


// 发送服务器
const char mqttPub[] = "anwei_iot_device_sub/";
// 订阅服务器
const char mqttSub[] = "anwei_iot_device_pub/";

/**
 * 读取EEPROM配置
 */
void readConfigType() {
    EEPROM.begin(782);
    uint8_t *p = (uint8_t *) (&configType);
    for (int i = 0; i < (int) sizeof(configType); ++i) {
        *(p + i) = EEPROM.read(i);
    }
    boolean isInit = ((String) configType.isInit).toInt() == 1;
    // 获取设备唯一ID
    deviceChipId = (String) EspClass::getChipId();

    // 首次写入设备,初始化数据
    if (!isInit) {
        // 拼接订阅和发布的主题
        // 发送服务器
        // 清零
        memset(&configType, 0, sizeof(configType));

        strcpy(mqttPubTopic, mqttPub); // 覆盖
        strcat(mqttPubTopic, deviceChipId.c_str()); //添加

        // 订阅服务器
        strcpy(mqttSubTopic, mqttSub);
        strcat(mqttSubTopic, deviceChipId.c_str());

        strcpy(configType.wifi_name, "");
        strcpy(configType.wifi_password, "");
        strcpy(configType.switch_1, "开关一");
        strcpy(configType.switch_2, "开关二");
        strcpy(configType.switch_3, "开关三");
        strcpy(configType.auto_wifi, "0");
        strcpy(configType.isInit, "1");
        strcpy(configType.mqttName, deviceChipId.c_str()); // 使用设备ID做MQTT账号
        strcpy(configType.mqttPaw, "123456"); // 默认密码
        strcpy(configType.mqttServer, ""); // MQTT服务器 设置自己的MQTT服务器  如 xxx.com
        strcpy(configType.mqttPort, "1883"); // MQTT端口
        strcpy(configType.mqttSub, mqttPubTopic);  // 设备订阅服务器的发送主题
        strcpy(configType.mqttPub, mqttSubTopic);  // 设备发送服务器订阅的主题  刚好和服务器相反
    }


    // 是否自动联网
    isAutoWiFi = ((String) configType.auto_wifi).toInt() == 1;
}

/**
 * 写入EEPROM配置
 */
void writeConfigType() {
    uint8_t *p = (uint8_t *) (&configType);
    for (int i = 0; i < (int) sizeof(configType); ++i) {
        EEPROM.write(i, *(p + i));
    }
    EEPROM.commit();
}

/**
 * 连接wifi
 */
void connectWiFi() {
    if (!isAutoWiFi)
        return;
    WiFi.disconnect();
    // 设置wifi为STA模式
    WiFi.mode(WIFI_STA);
    // wifi开始链接
    WiFi.begin(configType.wifi_name, configType.wifi_password);
    // 循环判断连接
    while (WiFi.status() != WL_CONNECTED) {
        delay(1000);
        timeTotal++;
        if (timeTotal >= CONNECT_WIFI_TIME_OUT)
            break;
    }
    timeTotal = 0;
    // 如果连接不上, 设置不自动连接
    if (WiFi.status() != WL_CONNECTED)
        isAutoWiFi = false;
}

/**
 * 配网等待
 */
void smartConfigWait() {
    while (true) {
        delay(2000);
        smartConfigTotal++;
        // 控制板载灯闪烁
        ledHandle();
        if (WiFi.smartConfigDone()) {
            // 获取wifi名称
            strcpy(configType.wifi_name, WiFi.SSID().c_str());
            // 获取wifi密码
            strcpy(configType.wifi_password, WiFi.psk().c_str());
            // 配网后设置启动自动连接wifi
            strcpy(configType.auto_wifi, "1");
            // 保存配置文件
            writeConfigType();
            break;
        }
        // 两分钟跳出
        if (smartConfigTotal >= 60) {
            smartConfigTotal = 0;
            break;
        }
    }

    // WiFi.stopSmartConfig();

    // 不管是否配网成功都重启模块
    // if (smartConfigTotal > 0) {
    //     EspClass::restart();
    // }
    setWorkMode(WORK_MODE_NULL);
}


/**
 * 连接mqtt
 */
void connectMQTT() {
    mqttClient.setServer(configType.mqttServer, ((String) configType.mqttPort).toInt());
    mqttClient.setCallback(mqttCallback);
    if (mqttClient.connect(deviceChipId.c_str(), deviceChipId.c_str(), configType.mqttPaw)) {
        // 订阅主题
        mqttClient.subscribe(configType.mqttSub, 1);
        if (isFirst) {
            setWorkMode(SEND_DATA_TO_SERVER);
        }
    }
}

/**
 * mqtt回调
 * @param topic
 * @param payload
 * @param length
 */
void mqttCallback(char *topic, byte *payload, int length) {
    char *result = new char[length + 1];
    memcpy(result, payload, length + 1);
    result[length] = 0;
    String str(result);
    DynamicJsonDocument data(1024);
    DeserializationError error = deserializeJson(data, result);
    if (error) {
        return;
    }
    // 模式
    int mode = data["mode"].as<int>();

    if (mode == UPDATE_FIRMWARE) {      // 更新固件
        String url = data["url"].as<String>();
        setWorkMode(WORK_MODE_UPDATE);
        updateFirmware(url);
    } else {
        if (mode == MODE_UPDATE_SWITCH_NAME) {  // 更新开关名称
            for (int i = 0; i < SWITCH_NUMBER; ++i) {
                switch (i) {
                    case 0:
                        strcpy(configType.switch_1, data["switchList"][i]["switchName"].as<String>().c_str());
                        break;
                    case 1:
                        strcpy(configType.switch_2, data["switchList"][i]["switchName"].as<String>().c_str());
                        break;
                    case 2:
                        strcpy(configType.switch_3, data["switchList"][i]["switchName"].as<String>().c_str());
                        break;
                }
            }
            // 保存到配置文件
            writeConfigType();
        } else if (mode == MODE_OPERATION) { // 操作开关状态
            boolean status[SWITCH_NUMBER];
            for (int i = 0; i < SWITCH_NUMBER; ++i) {
                status[i] = data["switchList"][i]["status"].as<boolean>();
            }
            for (int i = 0; i < SWITCH_NUMBER; ++i) {
                boolean s = digitalRead(switchPin[i]) == 1;
                if (status[i] != s) {
                    digitalWrite(switchPin[i], !digitalRead(switchPin[i]));
                }
            }
        } else if (mode == MODE_UPDATE_MQTT_PASSWORD) { // 更新MQTT密码
            strcpy(configType.mqttPaw, data["mqttPaw"].as<String>().c_str());
            writeConfigType();
            // 重新连接
            mqttClient.disconnect();
        } else if (mode == MODE_FEEDBACK_STATE) { // 即时上传状态
            setWorkMode(SEND_DATA_TO_SERVER);
        }
        // 发送数据到mqtt
        publishMQTT();
    }
}

/**
 * 发送数据到mqtt
 */
void publishMQTT() {
    DynamicJsonDocument data(1024);
    // 设备唯一ID
    data["deviceId"] = deviceChipId;
    // 设备类型
    data["type"] = "switch";
    // 开关数量
    data["switchNumber"] = SWITCH_NUMBER;
    // wifi信号强度
    data["rssi"] = WiFi.RSSI();
    // 固件版本
    data["firmwareVersion"] = FIRMWARE_VERSION;
    // 开关状态
    data["switchList"][SWITCH_NUMBER];
    for (int i = 0; i < SWITCH_NUMBER; ++i) {
        switch (i) {
            case 0:
                data["switchList"][i]["switchName"] = configType.switch_1;
                break;
            case 1:
                data["switchList"][i]["switchName"] = configType.switch_2;
                break;
            case 2:
                data["switchList"][i]["switchName"] = configType.switch_3;
                break;
        }
        data["switchList"][i]["status"] = digitalRead(switchPin[i]) == 1;
    }
    String output;
    serializeJson(data, output);
    mqttClient.publish(configType.mqttPub, output.c_str());
    setWorkMode(WORK_MODE_NULL);
}

/**
 * 更新固件
 */
void updateFirmware(const String &updateUrl) {
    WiFiClient client;
    ESPhttpUpdate.setLedPin(LED_BUILTIN, LOW);
    ESPhttpUpdate.update(client, updateUrl);
    /*** 以下代码调试时使用 */
    // t_httpUpdate_return ret = ESPhttpUpdate.update(client, updateUrl);
    // switch (ret) {
    //     case HTTP_UPDATE_FAILED: // 升级失败
    //         Serial.println("Update failed");
    //         break;
    //     case HTTP_UPDATE_NO_UPDATES:
    //         Serial.println("Update no update");
    //         break;
    //     case HTTP_UPDATE_OK:
    //         Serial.println("Update ok");
    //         break;
    // }
    setWorkMode(WORK_MODE_NULL);
}


// 设置wifi的ip地址
IPAddress apIP(192, 168, 4, 1);
IPAddress gateway(192, 168, 0, 1);
IPAddress subnet(255, 255, 255, 0);

// wifi热点账号
char ap_ssid[] = "anwei-iot";
// wifi热点密码
char ap_psw[] = "12345678a";
// 端口
const int SERVER_PORT = 8080;
// 设置wifi模块的最大连接数
#define MAX_CONNECT 5

WiFiServer wifi_server(SERVER_PORT);

WiFiClient *wifiClientList[MAX_CONNECT] = {nullptr};
WiFiClient wifi_client;

/**
 * 初始化AP模式
 */
void initSoftAP() {
    // 先断开wifi
    WiFi.disconnect();
    // 设置AP模式
    WiFi.mode(WIFI_AP);
    // 设置ip地址和掩码
    WiFi.softAPConfig(apIP, gateway, subnet);
    if (WiFi.softAP(ap_ssid, ap_psw)) {
        Serial.println("softAP start");
        wifi_server.begin();
    }
}

/**
 *  服务处理
 */
void serverLoop() {
    // 判断是否有新的连接
    wifi_client = wifi_server.available();
    // 判断这个连接是否有效
    if (wifi_client.connected()) {
        // 设置连接发送数据不延时
        wifi_client.setNoDelay(true);
        // 遍历连接池
        for (auto &i: wifiClientList) {
            if (i == nullptr || !i->connected()) {
                i = new WiFiClient(wifi_client);
                // 连接成功后直接发送设备的ID
                i->println(sendDataToAppHandle());
                break;
            }
        }
    }

    // 接收数据
    for (auto &i: wifiClientList) {
        if (i != nullptr && i->connected()) {
            if (i->available() > 0) {
                String data = i->readStringUntil('\n');
                if (readStringHandle(data)) {
                    i->println("success");
                }
            }
        }
    }
}

/**
 * 读取的数据处理
 * @param text
 */
boolean readStringHandle(String text) {
    DynamicJsonDocument data(1024);
    DeserializationError error = deserializeJson(data, text);
    if (error)
        return false;
    String t = "1";
    String f = "0";
    strcpy(configType.auto_wifi, data["autoWiFi"].as<boolean>() ? t.c_str() : f.c_str());
    strcpy(configType.wifi_name, data["wifiName"].as<String>().c_str());
    strcpy(configType.wifi_password, data["wifiPsw"].as<String>().c_str());
    strcpy(configType.mqttName, data["mqttName"].as<String>().c_str());
    strcpy(configType.mqttPaw, data["mqttPsw"].as<String>().c_str());
    strcpy(configType.mqttServer, data["mqttServer"].as<String>().c_str());
    strcpy(configType.mqttPort, data["mqttPort"].as<String>().c_str());
    strcpy(configType.mqttSub, data["mqttSub"].as<String>().c_str());
    strcpy(configType.mqttPub, data["mqttPub"].as<String>().c_str());
    writeConfigType();
    return true;
}

/**
 * 编辑 修改和添加设备的时候发送初始数据给APP
 */
String sendDataToAppHandle() {
    DynamicJsonDocument data(2048);
    data["deviceId"] = deviceChipId;
    data["wifiName"] = configType.wifi_name;
    data["wifiPsw"] = configType.wifi_password;
    data["autoWiFi"] = ((String) configType.auto_wifi).toInt() == 1;
    data["mqttName"] = configType.mqttName;
    data["mqttPsw"] = configType.mqttPaw;
    data["mqttServer"] = configType.mqttServer;
    data["mqttSub"] = configType.mqttSub;
    data["mqttPub"] = configType.mqttPub;
    data["mqttPort"] = ((String) configType.mqttPort).toInt();
    Serial.println(configType.mqttSub);
    Serial.println(configType.mqttPub);
    String output;
    serializeJson(data, output);
    return output;
}